IFM 3.3 Systemprogrammierung, W19
Am Ende der VL können/verstehen Sie:
Hardware ließe sich ohne ein Betriebssystem nicht sinnvoll nutzen. Das Betriebssystem muss auf der untersten Ebene die konkreten Hardware-Bausteine ansprechen (“Treiber”). Über dieser (hardwareabhängigen) Schicht liegen weitere Schichten, die immer weiter von der Hardware abstrahieren und am Ende allgemeine Schnittstellen bereitstellen, beispielsweise zur Kommunikation oder zur Speicherverwaltung.
Ohne weitere Hilfsprogramme, die sogenannten “Systemprogramme”, wäre ein Betriebssystem weitestgehend unbenutzbar. Es könnte vielleicht eine Folge von Nullen und Einsen auf einem schwarzen Bildschirm ausgeben … Die Systemprogramme stellen dem Nutzer beispielsweise ein Fenstersystem und einen Desktop sowie eine Shell und Editor etc. bereit. Dazu kommunizieren diese Programme über eine definierte Schnittstelle, die “Systemaufrufe”, mit dem Betriebssystem.1
Die Wahl für das Betriebsystem fällt in dieser Lehrveranstaltung auf Linux, nicht zuletzt, weil es Open Source und weit verbreitet ist. Damit wird auch die Wahl der Programmiersprache auf C und C++ festgelegt.
fork()/exec(), wait()/signal()create()/delete(), open()/close(), read()/write()request()/release(), read()/write()create()/delete(), send()/receive()
Quelle: (Kerrisk 2010, 46)
Systemaufruf-Mechanismus in der Praxis zu umständlich => Entsprechende Wrapper in C-Bibliothek vorhanden.
Normalerweise verwendet man den Begriff “Systemaufruf” sowohl für die “echten” Systemaufrufe als auch für den Aufruf des jeweiligen Wrappers aus der C-Bibliothek!
Quelle: Eraserhead1, Infinity0, Sav_vas (CC BY-SA 3.0, de.wikipedia.org)
Stammbaum Unix/Linux: Eindrucksvolle und weit zurückreichende Geschichte. Typisch für Unix sind die vielen Forks …
Bemerkenswert: Linux hat keine gemeinsamen Wurzeln mit Unix, auch wenn die POSIX-Schnittstellen eingehalten werden.
Interessant auch die Mischung der Lizenzmodelle von Closed Source über Shared Source zu Open Source.
Quelle: Unbekannt (gemeinfrei, de.wikipedia.org)
Am Anfang stand “Unics”, von Thompson und Ritchie (Bell Labs/AT&T) noch in Assembler geschrieben. Der der Legende nach wollte Ritchie Spiele auf seiner Workstation unter Unix schreiben/portieren, weshalb er ab 1971 die (für damalige Verhältnisse “Hoch-”) Sprache “C” entwickelte, mit in dem Unix dann neu implementiert wurde.
AT&T als Telefonkonzern durfte zunächst Unix nicht kommerziell vertreiben, weshalb das System zunächst kostenfreie an Universitäten verteilt wurde. Die University of California entwickelte daraus beispielsweise die “BSD”-Linie.
Ende der 1970er Jahre vermarktet AT&T das System dann selbst als “System-V”-Linie. Zahlreiche kommerzielle Ableger entstehen.
1983 startet Richard Stallman startet das GNU-Projekt (“GNU’s Not Unix”) mit dem Ziel, eine quelloffene Sammlung der wichtigen Systemtools (zb. Compiler: gcc) zu erstellen.
1985 Versuch der Standardisierung durch die IEEE: “POSIX” (Portable Operating System Interface). POSIX umfasst alle Aspekte von Unix (Kommandozeileninterpreter, Kommandos, Ein-/Ausgabe, …). Allerdings sind die Dokumente und eine Zertifizierung sehr teuer.
Quelle: Linuxmag.com (CC BY-SA 3.0, de.wikipedia.org)
1991 tritt der finnische Student Linus Torvalds auf den Plan. Er war unzufrieden mit dem Lehr-Unix “Minix” von A. Tanenbaum und startet die Entwicklung eines eigenen quelloffenen Systems, dessen Schnittstellen sich möglichst kompatibel zu Unix verhalten. Es entsteht ein monolithischer Kernel, der weitgehend in C geschrieben ist und zunächst nur für die Intel-i386 Plattform zur Verfügung steht.
Auch wenn die Kernelentwickler gern behaupten, der User Mode sei nur Testlast für den Kernel — ohne den User Mode kann man mit dem Linux-Kernel wenig anfangen. Es entstehen zahlreiche Linux-Distributionen, die neben dem Kernel eine Auswahl an diversen Systemprogrammen und weiteren Anwendungsprogrammen vorkompiliert für bestimmte Plattformen als Paket anbieten, beispielsweise Debian, Red Hat, Suse, Ubuntu.
Dabei sind zwei wichtige Paketformate entstanden, die alle wichtigen Distributionen bis heute (in abgewandelter Form) nutzen: deb von Debian und rpm von Red Hat. Die Distributionen übernehmen die Pflege der Softwarepakete und liefern entsprechende Patches aus, so dass man ein Linux-System relativ einfach über den jeweiligen Paketmanager aktuell halten kann.
gcc)Namensrechte an “Unix” liegen bei der Open Group
1971-73: Ritchie entwickelt die Sprache C
Ab 1979: Entwicklung von C++ durch Bjarne Stroustrup bei AT&T
=> C/C++ als ressourcenschonende hardwarenahe Sprache
In Java stellt die objektorientierte Programmierung den zentralen Abstraktionsmechanismus dar. Generics sind nur im Zusammenhang mit Klassen erlaubt und werden gewissermaßen zur Laufzeit umgesetzt.
In C++ wird zu Gunsten der Effizienz und der Minimierung des Ressourcenverbrauchs häufig auf Polymorphie und tiefe Klassenhierarchien verzichtet. Dagegen ist der Einsatz von C++-Templates stark verbreitet. Die C++-Templates werden zur Übersetzungszeit ausgewertet, weshalb man hier auch von “Meta-Programmierung” spricht.
Der Raspberry Pi (in unserem Fall ein Raspberry Pi 2, Model B) ist ein kleiner Computer, der nicht nur mit USB-Geräten, sondern auch mit diversen Sensoren und Aktoren über die zur Verfügung stehenden GPIO-Pins erweitert werden kann.
Es stehen verschiedene Betriebssysteme zur Verfügung. Diese werden auf der Speicherkarte des Raspis installiert (der Raspi hat keine Festplatte oder SSD). Im Praktikum nutzen wir die auf Debian basierende Linux-Distribution Raspbian, die bereits auf der Speicherkarte vorinstalliert ist.
Schauen Sie sich bitte die folgenden kurzen Videos und Dokumentationen an:
Für Sie sind folgende Anschlüsse relevant (vgl. folgende Abbildung):
Im Praktikum setzen wir einen sogenannten “Shield” ein (weiße Platine im Bild), der dauerhaft an den GPIO-Pins des Raspis angeschlossen ist. Der Shield stellt die Pins der GPIO-Leiste als Buchsen für richtige Steckverbinder zur Verfügung. Dadurch müssen Sie nicht mit einzelnen Kabelsträngen und eventuell einem Steckbrett arbeiten, sondern haben gut erreichbare Steck-Anschlüsse. Weiterhin stellt der Shield einen A/D-Wandler zur Verfügung, mit dem analoge Signale (in unserem Fall von einem Temperatursensor) eingelesen werden können. Der Raspberry Pi hat selbst keine analogen, sondern nur digitale Pins.
Über den Jumper (Spannungswähler) kann man die Versorgungsspannung einstellen, mit der die angeschlossenen Bauteile betrieben werden. Der Jumper verbindet jeweils zwei der drei Pins, in der gezeigten oberen Stellung für 3.3V (bzw. 5V, wenn die unteren beiden Pins verbunden sind). Der Jumper muss IMMER (wie in der Abbildung gezeigt) in der 3.3V-Position stecken! Anderenfalls können Sie die Bauteile zerstören!
Für die Ausgabe von Daten setzen wir letztem Jahr nicht mehr (wie auf der obigen Abbildung gezeigt) eine LED-Bar mit 10 LEDs ein, sondern die auf der folgenden Abbildung dargestellte vierstellige LED-Segmentanzeige:
Im ersten Projekt werden Sie entsprechende Funktionen zur Ansteuerung programmieren und als API zur Verfügung stellen. Beachten Sie die Position des Anschlusses (die Segmentanzeige wird an der selben Stelle angeschlossen wie die LED-Bar)!
Zum Messen von Temperaturen nutzen wir ab Projekt 3 einen Temperatursensor, der über den A/D-Wandler eingelesen wird. Beachten Sie auch hier die Position des Anschlusses!
Der Shield ist bereits angebaut und darf nicht entfernt werden! Um die Steckverbindungen der Kabel zu schonen, lassen Sie bitte die Segmentanzeige und den Temperatursensor ebenfalls immer eingesteckt.
sudo halt; Unterbrechung der StromzufuhrDemo Raspi
ACHTUNG: Der Spannungsumschalter (Jumper) auf dem Raspberry Pi Shield muss in der 3.3V Position gesteckt sein (vgl. folgende Abbildung).
Da die Schrift auf dem Shield nicht gut zu lesen ist: Der Jumper muss die oberen beiden Pins verbinden. Wenn der Raspberry Pi wie in der Abbildung um 90 Grad gedreht vor Ihnen liegt, sind es die beiden rechten Pins.
Im Idealfall ist der Jumper schon korrekt eingestellt. Prüfen Sie es bitte vor jedem Gebrauch, da ansonsten Schäden an den eingesetzten Sensoren und/oder Aktoren entstehen können.
Stecken Sie die SD-Karte gewaltfrei in den vorgesehenen Slot auf dem Raspberry Pi: Sie passt nur in einer Ausrichtung.
Verbinden Sie nun LAN-Kabel, Monitor, Tastatur und Maus mit dem Raspberry Pi. Dazu können Sie sich an den Thin Clients bedienen. Ein DVI/HDMI-Kabel steht an den Monitoren in den Laboren zur Verfügung.
Schauen Sie sich dazu auch die beiden kurzen Anleitungen Getting started with the Raspberry Pi und Setting up your Raspberry Pi an.
Nun stellen Sie die Stromversorgung des Raspberry Pi her.
Dazu stecken Sie den Micro-USB-Stecker des Stromkabels in den Micro-USB-Slot des Raspberry Pi und anschließend den Schukostecker des Stromkabels in die Steckdose.
Der Raspberry Pi wird jetzt booten.
Auf der Ihnen zur Verfügung gestellten Speicherkarte ist bereits Raspbian durch die Labormitarbeiter vorinstalliert.
Beim ersten Start müssen Sie Ihr System zunächst konfigurieren und aktualisieren sowie die benötigte Software nachinstallieren. Diese Punkte sind im Raspi-HowTo im Detail erklärt. Sie finden die PDF-Datei im Praktikumsbereich im ILIAS.
Falls Sie sich Ihre Installation einmal versehentlich “zerschießen”, können Sie sich Raspbian mit Hilfe des Raspbian Installation Guide frisch installieren.
Ihnen stehen verschiedene Möglichkeiten zur Verfügung, um mit dem Raspberry Pi zu arbeiten.
Sie können den Raspi wie einen “echten” Computer betreiben, d.h. immer LAN-Kabel, Monitor, Tastatur und Maus anschließen und “direkt” mit dem Raspi arbeiten.
Sie können aber auch mit SSH oder VNC/RDP “remote” arbeiten, d.h. der Raspi ist nur ans Netz angeschlossen und Sie greifen über Ihr Laptop (welches ebenfalls im Netz ist) darauf zu. Dazu muss “Ihr” Raspi einen eindeutigen Namen haben. Alternativ können Sie dem Raspi auch eine statische IP-Adresse konfigurieren und diesen mit dem Netzwerkkabel direkt an Ihren Rechner anschließen. Arbeiten Sie dazu die Dokumentation “REMOTE ACCESS” der Raspberry-Pi-Foundation durch.
Raspian ist im wesentlichen ein angepasstes Debian-Linux. Informieren Sie sich über die Nutzung: Documentation > Raspbian. Wegen der eingeschränkten Leistungsfähigkeit steht nur eine sehr einfache grafische Oberfläche (LXDE) zur Verfügung. Wichtig: Zugriffe auf die Segmentanzeige bzw. den Temperatursensor benötigen immer Root-Rechte, d.h. die entsprechenden Programme müssen mit einem vorangestellten sudo gestartet werden.
Der Raspberry Pi hat keinen Ein-/Aus-Schalter. Sie fahren das Betriebssystem mit dem Befehl sudo halt bzw. sudo shutdown -h now herunter und trennen anschließend das Netzteil vom Strom.
Achtung: Unterbrechen Sie nicht einfach nur die Stromversorgung (ohne vorher das Betriebssystem heruntergefahren zu haben). Dies schadet den Speicherkarten, möglicher Datenverlust ist die Folge.
/*
* cHelloWorld.c
*
*/
#include <stdio.h>
int main() {
printf("Hello World from C :-)\n");
return 0;
}Jedes (ausführbare) Programm hat genau eine main()-Funktion
Die main()-Funktion ist keine Methode einer Klasse
Java-Kommentare “//” gibt’s erst ab C99
Rückgabewert signalisiert Erfolg bzw. Fehler
return, wird durch den Compiler automatisch return 0 eingefügtEXIT_SUCCESS bzw. EXIT_FAILURE (in stdlib.h)Abspeichern mit der Endung “.c” (C) (oder “.cpp” oder “.cxx” oder “.cc” (C++))
Übersetzen und Linken in einem Arbeitsschritt
gcc HelloWorld.cgcc -ansi -pedantic -Wall -o helloworld HelloWorld.c
-ansi: Umschalten auf ANSI-Standard (C90)-pedantic: Warnungen bei Verletzung des ISO-C-Standards-Wall: Alle Warnungen anschalten-o: Setzen eines Namens für die ausführbare DateiAusführen mit: ./helloworld
.) ausgeführt werden sollKonsole: vl01/cHelloWorld.c
=> Manipulation d. Programmcodes, bevor Compiler diesen “sieht”
Zwei (von mehreren) Aufgaben:
Makrosubstitution: #define Makroname Ersatztext
Einfügen von Header-Dateien (und anderen Dateien):
#include "dateiname" sucht im aktuellen Ordner#include <dateiname> sucht im StandardverzeichnisSpäter beschäftigen wir uns noch ausführlich mit dem Präprozessor und Makros.
Konsole: vl01/defines.c
Makefile: Textdatei mit Regeln für make
ant, C/C++: make # Kommentar
Ziel1: AbhaengigkeitenListe1
Aktionen1
Ziel2: AbhaengigkeitenListe2
Aktionen2
# ... und so weiter :-)
# ACHTUNG:
# Vor den Aktionen <TAB> benutzen, keine Leerzeichen!!!
# Vorsicht mit Editor-Einstellungen!Bedeutung: Um das Ziel Ziel1 zu erzeugen, müssen alle Abhängigkeiten der Liste AbhaengigkeitenListe1 erfüllt sein. Dann werden die Aktionen in Aktionen1 durchgeführt, um Ziel1 zu erzeugen. Aber nur, falls das Ziel Ziel1 nicht existiert oder veraltet ist!
Falls die Abhängigkeiten nicht erfüllt sind, wird nach Regeln gesucht, um diese zu erzeugen. Das bedeutet, dass u.U. zunächst weitere Targets “gebaut” werden, bevor die Aktionenliste ausgeführt wird.
Die Ziele und Abhängigkeiten sind i.d.R. Dateien (müssen es aber nicht sein).
Annahme: Projekt besteht aus der Datei main.c
Passendes Makefile:
Bedeutung: Um das Ziel all zu erzeugen, muss die Abhängigkeit tollesProgramm erfüllt sein. Beachten Sie, dass im Beispiel all kein Dateiname ist, tollesProgramm dagegen schon.
Um tollesProgramm zu erzeugen, muss die Datei main.c vorhanden sein. Falls sie es nicht ist, bricht GNU Make mit einem Fehler ab, da es keine Regel zum Erstellen von main.c gibt.
Falls die Datei tollesProgramm nicht existiert oder aber älter ist als main.c, wird die Regel des Targets tollesProgramm ausgeführt, um die Datei tollesProgramm zu erzeugen: gcc -o tollesProgramm main.c.
Konsole: vl01/Makefile
make Sucht nach Datei mit dem Namen “GNUmakefile”, “makefile” oder “Makefile” und erzeugt das erste Ziel in der Datei
Konvention: Das erste Ziel hat den Namen all
make -f <datei> Sucht die Datei mit dem angegebenen Namen, erzeugt das erste Ziel in der Datei
make -f <datei> <ziel> Sucht die Datei mit dem angegebenen Namen, erzeugt das Ziel <ziel>
make <ziel> Sucht nach Datei mit dem Namen “GNUmakefile”, “makefile” oder “Makefile” und erzeugt das Ziel <ziel>
Begriffe: Betriebssystem, Systemaufruf, Systemprogrammierung
C/C++ sind enge Verwandte: kompilierte Sprachen
Kein OO in C: Keine Klassen, Methoden, …
Quellcode muss kompiliert werden: gcc -o output Quellcode.c
Präprozessor ersetzt Text bevor der Compiler arbeitet (include)
include: Einbinden von Bibliothekendefine: Definieren von Makros, zb. für Konstantenmain()-Funktion startet KontrollflussFunktionsweise einfachster Make-Files
Raspberry Pi: “vollwertiger” Kleinstcomputer
Nächste Woche: C: Datentypen, Funktionen, Datenstrukturen (struct, typedef)
Goll und Dausmann (2014)
Wolf (2009)
Breymann (2011)
man-Page: man 1 make
Für Interessierte der “Klassiker” (Kernighan und Ritchie 2000) und vom Erfinder von C++ (Stroustrup 2010, Kap. 27)
Breymann, U. 2011. Der C++ Programmierer. 2. Auflage. Hanser. ISBN: 978-3-446-42691-7.
Goll, J.; und M. Dausmann. 2014. C als erste Programmiersprache. 8. Aufl. Springer Fachmedien Wiesbaden. DOI: 10.1007/978-3-8348-2271-0.
Kernighan, B. W.; und D. Ritchie. 2000. The C Programming Language. Prentice Hall. ISBN: 978-0-1311-0362-7.
Kerrisk, M. 2010. The Linux Programming Interface. No Starch Press. ISBN: 978-1-5932-7220-3.
Stroustrup, B. 2010. Einführung in die Programmierung mit C++. Pearson Studium. ISBN: 978-3-8689-4005-3.
Wolf, J. 2009. C von A bis Z. 3. Auflage. Rheinwerk Computing. ISBN: 978-3-8362-1411-7. URL: http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/ (zugegriffen 03.08.2018).
piprintf()-pedantic, -ansi, -Wall, -Werror?Wie heißen typischerweise Makefiles?
Wie starten Sie GNU Make mit einem beliebig benannten Makefile?
Was sind Targets? Was sind Regeln?
Erklären Sie den Aufbau des folgenden fiktiven Makefiles. Was würde beim Aufruf von make und make all passieren?
pi?Machen Sie sich mit der Arbeitsumgebung unter Linux und den verwendeten Werkzeugen vertraut. Installieren Sie, falls noch nicht geschehen, die GNU-Compilercollection (gcc, g++, gdb) und eine für Sie passende IDE. Probieren Sie ruhig verschiedene IDEs aus, um die für Sie geeignete herauszufinden.
-Wall, -Werror, -O3)?Anmerkung: In der Abbildung wird der Begriff “Systemaufruf” nur im Gerätestack verwendet. Üblicherweise (und auch hier in der Veranstaltung) meint man damit die komplette Schnittstelle vom Betriebssystem/Kernel zum Nutzer.↩︎